package com.gosling.cloudspace.tools;

import cn.hutool.crypto.digest.MD5;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5Tool extends MD5 {
    private static char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    private static char[] DIGITS_LARGE = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

    public static String MD5Small(String str) {
        return md5(str, DIGITS_LOWER);
    }

    public static String MD5Large(String large) {
        return md5(large, DIGITS_LARGE);
    }

    /***
     * 错误的加密方式
     * 可能会出现少一位的情况（抹掉了0）
     *
     * @param path
     * @return
     */
    @Deprecated
    public static String getMd5ByFilePath(String path) {
        String value = "";
        File file = new File(path);
        FileInputStream in = null;
        FileChannel fileChannel = null;
        try {
            in = new FileInputStream(file);
            fileChannel = in.getChannel();
            MappedByteBuffer byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, file.length());
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(byteBuffer);
            BigInteger bi = new BigInteger(1, md5.digest());
            value = bi.toString(16);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IOTool.closeQuietly(fileChannel);
            IOTool.closeQuietly(in);
        }
        return value;
    }

    public static String getMd5ByFail(String data) {
        String value = "";
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(data.getBytes());
            BigInteger bi = new BigInteger(1, md5.digest());
            value = bi.toString(16);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return value;
    }

    /***
     * 修改过后的加密方式
     * 便于异常情况关流
     *
     * @param file
     * @return
     */
    public static String getMd5ByFile(File file) {
        String md5Hex = "";
        FileInputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
            md5Hex = DigestUtils.md5Hex(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOTool.closeQuietly(inputStream);
        }
        return md5Hex;
    }

    public static String md5(String large, char[] hexDigits) {
        String md5 = "";
        try {
            MessageDigest mdInst = MessageDigest.getInstance("MD5");
            mdInst.update(large.getBytes(StandardCharsets.UTF_8));
            byte[] md = mdInst.digest();
            int j = md.length;
            char[] str = new char[j * 2];
            int k = 0;
            for (byte byte0 : md) {
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            md5 = new String(str);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return md5;
    }

    public static String getMd5Code(File attachFile) {
        String md5Code = "";
        try {
            if (attachFile.exists() && attachFile.isFile()) {
                String message = FileUtils.readFileToString(attachFile, "utf-8");
                md5Code = generate(message);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return md5Code;
    }

    public static String generate(String str) {
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] bs = md5.digest(str.getBytes());
            StringBuilder sb = new StringBuilder(40);
            for (byte x : bs) {
                if ((x & 0xFF) >> 4 == 0) {
                    sb.append("0").append(Integer.toHexString(x & 0xFF));
                } else {
                    sb.append(Integer.toHexString(x & 0xFF));
                }
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException noSuchAlgorithmException) {
            return "";
        }
    }

    /**
     * MD5加密
     *
     * @param str
     * @return
     */
    public final static String encode(String str) {
        try {
            //创建具有指定算法名称的摘要
            MessageDigest md = MessageDigest.getInstance("MD5");
            //使用指定的字节数组更新摘要
            md.update(str.getBytes());
            //进行哈希计算并返回一个字节数组
            byte[] mdBytes = md.digest();
            StringBuilder hash = new StringBuilder();
            //循环字节数组
            for (byte mdByte : mdBytes) {
                int temp;
                //如果有小于0的字节,则转换为正数
                if (mdByte < 0) {
                    temp = 256 + mdByte;
                } else {
                    temp = mdByte;
                }
                if (temp < 16) {
                    hash.append("0");
                }
                //将字节转换为16进制后，转换为字符串
                hash.append(Integer.toString(temp, 16));
            }
            return hash.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * @param numStr
     * @param salt
     * @return
     * @email 1793327033@qq.com
     * @classname MD5Utils
     * @author: osc_lihonglin
     * @date: 2021/6/24 9:38
     */
    public static String encodeWithSalt(String numStr, String salt) {
        return encode(encode(numStr) + salt);
    }


    /**
     * MD5加密函数，输入字符串并返回其MD5哈希值（十六进制表示）
     *
     * @param input 待加密的字符串
     * @return 加密后的MD5哈希值（字符串形式）
     */
    public static String funMD5(String input) {
        StringBuilder hexValue = new StringBuilder();
        try {
//            // 将字符串转换为字符数组
//            char[] charArray = input.toCharArray();
//            // 将字符数组转换为字节数组
//            byte[] byteArray = new byte[charArray.length];
//            for (int i = 0; i < charArray.length; i++) {
//                byteArray[i] = (byte) charArray[i];
//            }

            byte[] bytes = input.getBytes();
            // 创建MessageDigest实例，使用MD5算法
            MessageDigest md5Digest = MessageDigest.getInstance("MD5");
            // 对字节数组执行MD5摘要计算
            byte[] md5Bytes = md5Digest.digest(bytes);
            // 遍历MD5字节数组，将其转换为16进制表示形式并添加到结果字符串中
            for (byte b : md5Bytes) {
                int value = b & 0xff;
                if (value < 16) {
                    hexValue.append('0');
                }
                hexValue.append(Integer.toHexString(value));
            }
            // 返回最终的MD5哈希值（十六进制表示）
            return hexValue.toString();
        } catch (NoSuchAlgorithmException e) {
            // 如果捕获到MD5算法未找到异常，则打印堆栈跟踪信息
            e.printStackTrace();
            // 在出现异常的情况下返回空字符串作为默认行为（实际应用中可按需处理）
            return "";
        }
    }
}
